/*
 *  This file is part of the Haven & Hearth game client.
 *  Copyright (C) 2009 Fredrik Tolf <fredrik@dolda2000.com>, and
 *                     Björn Johannessen <johannessen.bjorn@gmail.com>
 *
 *  Redistribution and/or modification of this file is subject to the
 *  terms of the GNU Lesser General Public License, version 3, as
 *  published by the Free Software Foundation.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  Other parts of this source tree adhere to other copying
 *  rights. Please see the file `COPYING' in the root directory of the
 *  source tree for details.
 *
 *  A copy the GNU Lesser General Public License is distributed along
 *  with the source tree of which this file is a part in the file
 *  `doc/LPGL-3'. If it is missing for any reason, please see the Free
 *  Software Foundation's website at <http://www.fsf.org/>, or write
 *  to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 *  Boston, MA 02111-1307 USA
 */

package openhaven.control.net;

import java.awt.Color;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import openhaven.control.message.RichMessage;
import openhaven.model.Coord;

/**
 * Stolen from original client, Coord stuff removed
 */
public class Message extends RichMessage implements java.io.Serializable
{
    private static final long serialVersionUID = 1L;
    public static final int RMSG_NEWWDG = 0;
    public static final int RMSG_WDGMSG = 1;
    public static final int RMSG_DSTWDG = 2;
    public static final int RMSG_MAPIV = 3;
    public static final int RMSG_GLOBLOB = 4;
    public static final int RMSG_PAGINAE = 5;
    public static final int RMSG_RESID = 6;
    public static final int RMSG_PARTY = 7;
    public static final int RMSG_SFX = 8;
    public static final int RMSG_CATTR = 9;
    public static final int RMSG_MUSIC = 10;
    public static final int RMSG_TILES = 11;

    enum MessageType
    {
        RMSG_NEWWDG, RMSG_WDGMSG, RMSG_DSTWDG, RMSG_MAPIV, RMSG_GLOBLOB, RMSG_PAGINAE, RMSG_RESID, RMSG_PARTY, RMSG_SFX, RMSG_CATTR, RMSG_MUSIC, RMSG_TILES
    }

    public static final Map<Integer, MessageType> messageTypeBinding = new HashMap<Integer, MessageType>();
    static
    {
        messageTypeBinding.put(0, MessageType.RMSG_NEWWDG);
        messageTypeBinding.put(1, MessageType.RMSG_WDGMSG);
        messageTypeBinding.put(2, MessageType.RMSG_DSTWDG);
        messageTypeBinding.put(3, MessageType.RMSG_MAPIV);
        messageTypeBinding.put(4, MessageType.RMSG_GLOBLOB);
        messageTypeBinding.put(5, MessageType.RMSG_PAGINAE);
        messageTypeBinding.put(6, MessageType.RMSG_RESID);
        messageTypeBinding.put(7, MessageType.RMSG_PARTY);
        messageTypeBinding.put(8, MessageType.RMSG_SFX);
        messageTypeBinding.put(9, MessageType.RMSG_CATTR);
        messageTypeBinding.put(10, MessageType.RMSG_MUSIC);
        messageTypeBinding.put(11, MessageType.RMSG_TILES);
    }

    public static final int T_END = 0;
    public static final int T_INT = 1;
    public static final int T_STR = 2;
    public static final int T_COORD = 3;
    public static final int T_COLOR = 6;

    public int type;
    public byte[] blob;
    public long last = 0;
    public int seq;
    int off = 0;

    public Message(final int type, final byte[] blob)
    {
        this.type = type;
        this.blob = blob;
    }

    public Message(final int type, final byte[] blob, final int offset, final int len)
    {
        this.type = type;
        this.blob = new byte[len];
        System.arraycopy(blob, offset, this.blob, 0, len);
    }

    public Message(final int type)
    {
        this.type = type;
        blob = new byte[0];
    }

    @Override
    public boolean equals(final Object o2)
    {
        if (!(o2 instanceof Message))
        {
            return (false);
        }
        final Message m2 = (Message) o2;
        if (m2.blob.length != blob.length)
        {
            return (false);
        }
        for (int i = 0; i < blob.length; i++)
        {
            if (m2.blob[i] != blob[i])
            {
                return (false);
            }
        }
        return (true);
    }

    @Override
    public Message clone()
    {
        return (new Message(type, blob));
    }

    public Message derive(final int type, final int len)
    {
        final int ooff = off;
        off += len;
        return (new Message(type, blob, ooff, len));
    }

    public void addbytes(final byte[] src)
    {
        final byte[] n = new byte[blob.length + src.length];
        System.arraycopy(blob, 0, n, 0, blob.length);
        System.arraycopy(src, 0, n, blob.length, src.length);
        blob = n;
    }

    public void adduint8(final int num)
    {
        addbytes(new byte[] { BitFiddlingUtils.sb(num) });
    }

    public void adduint16(final int num)
    {
        final byte[] buf = new byte[2];
        BitFiddlingUtils.uint16e(num, buf, 0);
        addbytes(buf);
    }

    public void addint32(final int num)
    {
        final byte[] buf = new byte[4];
        BitFiddlingUtils.int32e(num, buf, 0);
        addbytes(buf);
    }

    public void addstring2(final String str)
    {
        byte[] buf;
        try
        {
            buf = str.getBytes("utf-8");
        }
        catch (final java.io.UnsupportedEncodingException e)
        {
            throw (new RuntimeException(e));
        }
        addbytes(buf);
    }

    public void addstring(final String str)
    {
        addstring2(str);
        addbytes(new byte[] { 0 });
    }

    // public void addcoord(final Coord c)
    // {
    // addint32(c.x);
    // addint32(c.y);
    // }

    public void addlist(final Object... args)
    {
        for (final Object o : args)
        {
            if (o instanceof Integer)
            {
                adduint8(T_INT);
                addint32(((Integer) o).intValue());
            }
            else if (o instanceof String)
            {
                adduint8(T_STR);
                addstring((String) o);
            }
            // else if (o instanceof Coord)
            // {
            // adduint8(T_COORD);
            // addcoord((Coord) o);
            // }
        }
    }

    public boolean eom()
    {
        return (off >= blob.length);
    }

    public int uint8()
    {
        return (BitFiddlingUtils.ub(blob[off++]));
    }

    public int uint16()
    {
        off += 2;
        return (BitFiddlingUtils.uint16d(blob, off - 2));
    }

    public int int32()
    {
        off += 4;
        return (BitFiddlingUtils.int32d(blob, off - 4));
    }

    public String string()
    {
        final int[] ob = new int[] { off };
        final String ret = BitFiddlingUtils.strd(blob, ob);
        off = ob[0];
        return (ret);
    }

    public Coord coord()
    {
        return (new Coord(int32(), int32()));
    }

    public Color color()
    {
        return (new Color(uint8(), uint8(), uint8(), uint8()));
    }

    public Object[] list()
    {
        final ArrayList<Object> ret = new ArrayList<Object>();
        while (true)
        {
            if (off >= blob.length)
            {
                break;
            }
            final int t = uint8();
            if (t == T_END)
            {
                break;
            }
            else if (t == T_INT)
            {
                ret.add(int32());
            }
            else if (t == T_STR)
            {
                ret.add(string());
            }
            // else if (t == T_COORD)
            // {
            // ret.add(coord());
            // }
            else if (t == T_COLOR)
            {
                ret.add(color());
            }
        }
        return (ret.toArray());
    }

    @Override
    public String toString()
    {
        String ret = "";
        for (final byte b : blob)
        {
            ret += String.format("%02x ", b);
        }
        return ("Message(" + type + "): " + ret);
    }

}
